# -*- coding: utf-8 -*-
# @Author  : 张佳玉
# @Time    : 2023/7/11 11:30
# @Function: LSB

from PIL import Image
import math


def dec_to_bin(information_dec):
    """
    将十进制数转换成二进制串
    @param information_dec:十进制字符串
    @return:二进制字符串
    """
    information = int(information_dec)
    information_bin_list = []
    while information:
        information_bin_list.append(information % 2)
        information = information // 2
    # 倒序写入
    information_bin = ''.join([str(x) for x in information_bin_list[::-1]])
    return information_bin


def fill_with_zero(str):
    """
    返回指定长度的字符串，原字符串右对齐，前面填充0。
    @param str:带填补的字符串
    @return:八位长度的字符串
    """
    return str.zfill(8)


def secret_information(secret_file_name):
    """
    提取要隐藏的秘密信息,转换成一个二进制串
    @param secret_file_name:秘密信息所在的文件名
    @return:秘密信息二进制串
    """
    secret_file = open(secret_file_name, 'rb')
    secret_information_bin = ""
    secret_information_bytes = secret_file.read()
    # print("type= ", type(secret_information_bytes))
    #print("secret_information=", secret_information_bytes)
    for i in range(len(secret_information_bytes)):
        # 逐个字节将要隐藏的文件内容转换为二进制，并拼接起来
        # 1.secret_information_bytes类型为bytes,使用bin()函数转换为二进制
        # 2.由于bin()函数转换二进制后，二进制字符串的前面会有"0b"来表示这个字符串是二进制形式，所以用replace()替换为空
        # 3.又由于ascii码转换二进制后是七位，而正常情况下每个字符由8位二进制组成，所以使用自定义函数plus将其填充为8位
        secret_information_bin = secret_information_bin + \
                                 fill_with_zero(bin(secret_information_bytes[i]).replace('0b', ''))
    print("要隐藏的秘密信息长度为： ", len(secret_information_bin))
    print("secret_information=", secret_information_bin)
    secret_file.close()
    return secret_information_bin


def lsb(pixel):
    """
    读取像素值的最低位
    @param pixel: 像素，二进制
    @return:像素值的最低位
    """
    return pixel[len(pixel) - 1]


def f(pixel_1, pixel_2):
    """
    两个像素值的函数f
    @param pixel_1:
    @param pixel_2:
    @return:
    """
    return lsb(str(dec_to_bin(int(math.floor(pixel_1 / 2.0) + pixel_2))))


def lsb_matching_revisited(carrier_image, secret_image, storage_image):
    """
    LSB Matching Revisited
    @param carrier_image: 载体图片路径
    @param secret_image: 秘密信息文件路径
    @param storage_image: 加密图片保存的路径
    @return:
    """
    # 转换成灰度图
    grayscale_image = Image.open(carrier_image).convert('L')

    # 获取图片的宽和高
    width = grayscale_image.size[0]
    print("width:", str(width))
    height = grayscale_image.size[1]
    print("height:", str(height))

    # 获取秘密信息
    hide_information = secret_information(secret_image)
    hide_information_len = len(hide_information)

    count = 0

    for w in range(0, width):
        for h in range(0, height):
            if count == hide_information_len:
                break

            # 像素值为十进制
            x_1 = grayscale_image.getpixel((w, h))
            x_2 = grayscale_image.getpixel((w + 1, h))

            m_1 = hide_information[count]
            m_2 = hide_information[count + 1]

            print("隐藏秘密信息的第", count + 1, "和第", count + 2, "个比特")
            print("第", count + 1, "个像素为：", dec_to_bin(x_1))
            print("第", count + 2, "个像素为：", dec_to_bin(x_2))

            print("第", count + 1, "个秘密信息为：", m_1)
            print("第", count + 2, "个秘密信息为：", m_2)
            print("lsb(xi) = ", lsb(dec_to_bin(x_1)))
            print("f(xi,xi+1) = ", f(x_1, x_2))
            print("f(xi-1,xi+1) = ", f(x_1 - 1, x_2))

            # LSB Matching Revisited
            if m_1 == lsb(dec_to_bin(x_1)):
                if m_2 != f(x_1, x_2):
                    y2 = x_2 + 1
                else:
                    y2 = x_2
                y1 = x_1
            else:
                if m_2 == f(x_1 - 1, x_2):
                    y1 = x_1 + -1
                else:
                    y1 = x_1 + 1
                y2 = x_2
            grayscale_image.putpixel((w, h), y1)
            grayscale_image.putpixel((w + 1, h), y2)
            count += 2
            print("count ", count)
    grayscale_image.show()
    grayscale_image.save(storage_image)
    print("信息隐藏完成")


if __name__ == '__main__':
    carrier_image_test = "E:/code/1_LSB Matching Revisited/lsb/LenaRGB.bmp"
    storage_image_test = "E:/code/1_LSB Matching Revisited/lsb/lena_with_secret.jpg"
    secret_image_test = "E:/code/1_LSB Matching Revisited/lsb/copyright.jpg"
    lsb_matching_revisited(carrier_image_test, secret_image_test, storage_image_test)